<div x-data="alpineAgents()" x-init="initPage()" id="main-content">

    <!-- PAGE HEADER -->

    <div x-ref="header">
        <h2>Agents</h2>
        <p>
            You must deploy at least 1 agent in order to run an operation. Groups are collections of agents so hosts can be compromised simultaneously.
        </p>
    </div>
    <hr>

    <!-- AGENTS TABLE AND CONFIGURATION -->

    <div>
        <div class="columns">
            <div class="column is-4 buttons m-0 is-flex is-justify-content-flex-start">
                <button class="button is-primary is-small level-item" @click="showDeployModal = true">
                    <span class="icon"><i class="fas fa-plus"></i></span>
                    <span>Deploy an agent</span>
                </button>
                <button class="button is-primary is-small level-item" @click="showConfigModal = true">
                    <span class="icon"><i class="fas fa-cog"></i></span>
                    <span>Configuration</span>
                </button>
            </div>
            <div class="column is-4 m-0 is-flex is-justify-content-center">
                <span class="level-item tag is-medium m-0" x-on:mouseenter="isHoveringAgentCount = true" x-on:mouseleave="isHoveringAgentCount = false">
                    <span class="mr-4 has-text-success" x-show="isHoveringAgentCount">
                        <span class="has-text-weight-bold" x-text="agents.filter((a) => getAgentStatus(a) === 'alive' || getAgentStatus(a) === 'pending kill').length"></span>&nbsp;alive&nbsp;·
                        <span class="has-text-weight-bold" x-text="agents.filter((a) => a.trusted).length"></span>&nbsp;trusted
                    </span>
                    <strong x-text="agents.length"></strong>&nbsp;agent<span x-show="agents.length !== 1">s</span>
                    <span class="ml-4 has-text-warning" x-show="isHoveringAgentCount">
                        <span class="has-text-weight-bold" x-text="agents.filter((a) => getAgentStatus(a) === 'dead').length"></span>&nbsp;dead&nbsp;·
                        <span class="has-text-weight-bold" x-text="agents.filter((a) => !a.trusted).length"></span>&nbsp;untrusted
                    </span>
                </span>
            </div>
            <div class="column is-4 m-0 is-flex is-justify-content-flex-end">
                <div class="dropdown is-right is-hoverable">
                    <div class="dropdown-trigger">
                        <button class="button is-small is-primary" aria-haspopup="true" aria-controls="bulk-actions">
                            <span>Bulk Actions</span>
                            <span class="icon is-small"><i class="fas fa-angle-down" aria-hidden="true"></i></span>
                        </button>
                    </div>
                    <div class="dropdown-menu" id="bulk-actions" role="menu">
                        <div class="dropdown-content">
                            <a href="#" class="dropdown-item" @click="confirmAlt('Are you sure you want to remove all dead agents?', removeDeadAgents)">
                                Remove dead agents
                            </a>
                            <a href="#" class="dropdown-item" @click="confirmAlt('Are you sure you want to remove all agents? Agents that beacon after they are removed will reappear.', removeAllAgents)">
                                Remove all agents
                            </a>
                            <a href="#" class="dropdown-item has-text-danger" @click="confirmAlt('You are about to kill all agents. Are you sure?', killAllAgents)">
                                Kill all agents
                            </a>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <table class="table is-striped is-fullwidth" x-show="agents.length">
            <thead>
                <tr>
                    <th>id (paw)</th>
                    <th>host</th>
                    <th>group</th>
                    <th>platform</th>
                    <th>contact</th>
                    <th>pid</th>
                    <th>privilege</th>
                    <th>status</th>
                    <th>last seen</th>
                    <td></td>
                </tr>
            </thead>
            <tbody>
                <template x-for="(agent, index) of agents" :key="agent.paw">
                    <tr @click="selectedAgent = agent; showDetailsModal = true;" class="pointer">
                        <td x-text="agent.paw"></td>
                        <td x-text="agent.host"></td>
                        <td x-text="agent.group"></td>
                        <td x-text="agent.platform"></td>
                        <td x-text="agent.contact"></td>
                        <td x-text="agent.pid"></td>
                        <td x-text="agent.privilege"></td>
                        <td>
                            <span x-text="getAgentStatus(agent)" x-bind:class="{ 'has-text-warning': getAgentStatus(agent) === 'dead', 'has-text-success': getAgentStatus(agent) === 'alive', 'has-text-info': getAgentStatus(agent) === 'pending kill' }"></span><span>,</span>
                            <span x-text="agent.trusted ? 'trusted' : 'untrusted'" x-bind:class="{ 'has-text-warning': !agent.trusted, 'has-text-success': agent.trusted }"></span>
                        </td>
                        <td x-text="getHumanFriendlyTimeISO8601(agent.last_seen)" x-bind:data-tooltip="agent.last_seen"></td>
                        <td class="has-text-centered"><button class="delete is-white" @click.stop="removeAgent(agent.paw, index)"></button></td>
                    </tr>
                </template>
            </tbody>
        </table>
        <div class="has-text-centered content" x-show="!agents.length">
           <p>No deployed agents</p> 
        </div>
    </div>

    <!-- MODALS -->

    <div class="modal" x-bind:class="{ 'is-active': showDeployModal }">
        <div class="modal-background" @click="closeDeployModal()"></div>
        <div class="modal-card wide">
            <header class="modal-card-head">
                <p class="modal-card-title">Deploy an agent</p>
            </header>
            <section class="modal-card-body">
                <form class="has-text-centered">
                    <div class="field">
                        <label class="label">Agent</label>
                        <div class="control">
                            <div class="select is-small">
                                <select x-model="selectedAbilityId" x-on:change="selectAbility()">
                                    <option disabled selected value="">Choose an agent</option>
                                    <template x-for="ability of deploymentAbilities" :key="ability.ability_id">
                                        <option x-bind:value="ability.ability_id" x-text="`${ability.name} | ${ability.description}`"></option>
                                    </template>
                                </select>
                            </div>
                        </div>
                    </div>
                    <div class="field" x-show="selectedAbilityId">
                        <label class="label">Platform</label>
                        <div class="control is-flex is-flex-direction-row is-justify-content-center">
                            <div class="has-text-centered platform" x-bind:class="{ 'selected': selectedPlatform === 'all' }" @click="changePlatform('all')" x-show="selectedPlatform">
                                <span class="icon is-large"><i class="far fa-2x fa-circle"></i></span>
                                <br> all 
                            </div>
                            <div class="has-text-centered platform" x-bind:class="{ 'selected': selectedPlatform === 'linux' }" x-show="platforms.includes('linux')" @click="changePlatform('linux')">
                                <span class="icon is-large"><i class="fab fa-2x fa-linux"></i></span>
                                <br> linux 
                            </div>
                            <div class="has-text-centered platform" x-bind:class="{ 'selected': selectedPlatform === 'windows' }" x-show="platforms.includes('windows')" @click="changePlatform('windows')">
                                <span class="icon is-large"><i class="fab fa-2x fa-windows"></i></span>
                                <br> windows 
                            </div>
                            <div class="has-text-centered platform" x-bind:class="{ 'selected': selectedPlatform === 'darwin' }" x-show="platforms.includes('darwin')" @click="changePlatform('darwin')">
                                <span class="icon is-large"><i class="fab fa-2x fa-apple"></i></span>
                                <br> darwin 
                            </div>
                        </div>
                    </div>
                </form>
                <form class="command-fields">
                    <template x-for="field of agentFields" :key="field.name">
                        <div class="field is-horizontal">
                            <div class="field-label is-normal">
                                <label class="label" x-text="field.name"></label>
                            </div>
                            <div class="field-body">
                                <div class="field has-addons">
                                    <div class="control is-expanded">
                                        <input class="input is-small" type="text" x-model="field.value">
                                    </div>
                                    <div class="control">
                                        <a class="button is-small has-tooltip-arrow" data-tooltip="Reset to default" @click="field.value = agentFieldConfig[field.name]">
                                            <span class="icon"><i class="fas fa-undo"></i></span>
                                        </a>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </template>
                </form>
                <hr x-show="filteredCommands.length">
                <div>
                    <template x-for="command of filteredCommands" :key="command.command">
                        <div class="command-container container">
                            <div class="tags are-medium has-addons">
                                <span class="tag is-black">
                                    <span class="icon">
                                        <i class="fab" x-bind:class="{ 'fa-windows': command.platform === 'windows', 'fa-linux': command.platform === 'linux', 'fa-apple': command.platform === 'darwin' }"></i>
                                    </span>
                                </span>
                                <span class="tag is-dark" x-text="command.executor"></span>
                                <span class="tag" x-text="command.description"></span>
                            </div>
                            <div class="box container">
                                <pre class="box" x-text="getCommandField(command.command)"></pre>
                                <a class="button is-small is-outlined cmd-copy-button" @click="copyCommandToClipboard(getCommandField(command.command))" x-show="window.isSecureContext">
                                    <span class="icon"><i class="far fa-lg fa-copy"></i></span>
                                    <span>Copy</span>
                                </a>
                            </div>
                            <p class="has-text-centered" x-show="command.variations.length"><b>Variations</b></p>
                            <template x-for="variation of command.variations" :key="variation.command">
                                <div class="content">
                                    <div class="tags are-medium has-addons">
                                        <span class="tag is-black">
                                            <span class="icon">
                                                <i class="fab" x-bind:class="{ 'fa-windows': command.platform === 'windows', 'fa-linux': command.platform === 'linux', 'fa-apple': command.platform === 'darwin' }"></i>
                                            </span>
                                        </span>
                                        <span class="tag is-dark" x-text="command.executor"></span>
                                        <span class="tag" x-text="variation.description"></span>
                                    </div>
                                    <div class="box container">
                                        <pre class="box" x-text="getCommandField(variation.command).replaceAll(';', ';\n')"></pre>
                                        <a class="button is-small is-outlined cmd-copy-button" @click="copyCommandToClipboard(getCommandField(variation.command))" x-show="window.isSecureContext">
                                            <span class="icon"><i class="far fa-lg fa-copy"></i></span>
                                            <span>Copy</span>
                                        </a>
                                    </div>
                                </div>
                            </template>
                        </div>
                    </template>
                </div>
            </section>
            <footer class="modal-card-foot">
                <nav class="level">
                    <div class="level-left"></div>
                    <div class="level-right">
                        <div class="level-item">
                            <button class="button is-small" @click="closeDeployModal()">Close</button>
                        <div>
                    </div>
                </nav>
                
            </footer>
        </div>
    </div>

    <div class="modal" x-bind:class="{ 'is-active': showConfigModal }">
        <div class="modal-background" @click="showConfigModal = false"></div>
        <div class="modal-card">
            <header class="modal-card-head">
                <p class="modal-card-title">Agent Configuration</p>
            </header>
            <section class="modal-card-body">
                <table>
                    <col width="35%">
                    <col width="65%">
                    <tbody>
                        <tr>
                            <th class="has-text-right pt-3">
                                <span class="has-tooltip-multiline has-tooltip-bottom" data-tooltip="Set the minimum and maximum seconds the agent will take to beacon home.">Beacon Timers (s)</span>
                            </th>
                            <td>
                                <div class="is-align-items-center is-flex is-align-items-center">
                                    <span class="top-right-anchor">
                                        <label>min</label>
                                        <input class="input is-small is-small-number" type="number" placeholder="30" x-model="agentConfig.sleep_min" x-bind:class="{ 'is-danger': fieldErrorsConfig.includes('sleep_min') }">
                                    </span>
                                    <span class="top-right-anchor">
                                        <label>max</label>
                                        <input class="input is-small is-small-number" type="number" placeholder="60" x-model="agentConfig.sleep_max" x-bind:class="{ 'is-danger': fieldErrorsConfig.includes('sleep_max') }">                                   
                                    </span>
                                </div>
                                <p x-show="fieldErrorsConfig.includes('sleep_min') || fieldErrorsConfig.includes('sleep_max')" class="help is-danger">This field is required.</p> 
                            </td>
                        </tr>
                        <tr>
                            <th class="has-text-right pt-3">
                                <span class="has-tooltip-multiline has-tooltip-bottom" data-tooltip="Set the number of seconds to wait, once the server is unreachable, before killing an agent.">Watchdog Timer (s)</span>
                            </th>
                            <td>
                                <input class="input is-small is-small-number" type="number" placeholder="0" x-model="agentConfig.watchdog" x-bind:class="{ 'is-danger': fieldErrorsConfig.includes('watchdog') }">
                                <p x-show="fieldErrorsConfig.includes('watchdog')" class="help is-danger">This field is required.</p>
                            </td>
                        </tr>
                        <tr>
                            <th class="has-text-right pt-3">
                                <span class="has-tooltip-multiline" data-tooltip="Set the number of seconds to wait before marking a missing agent as untrusted. Operations will not generate new links for untrusted agents.">Untrusted Timer (s)</span> *
                            </th>
                            <td>
                                <input class="input is-small is-small-number" type="number" placeholder="90" x-model="agentConfig.untrusted_timer" x-bind:class="{ 'is-danger': fieldErrorsConfig.includes('untrusted_timer') }">
                                <p x-show="fieldErrorsConfig.includes('untrusted_timer')" class="help is-danger">This field is required.</p>
                            </td>
                        </tr>
                        <tr>
                            <th class="has-text-right pt-3">
                                <span class="has-tooltip-multiline" data-tooltip="The base name of newly-spawned agents. If necessary, an extension will be added when an agent is created.">Implant Name</span>
                            </th>
                            <td>
                                <input class="input is-small" type="text" placeholder="splunkd" x-model="agentConfig.implant_name" x-bind:class="{ 'is-danger': fieldErrorsConfig.includes('implant_name') }">
                                <p x-show="fieldErrorsConfig.includes('implant_name')" class="help is-danger">This field is required.</p>
                            </td>
                        </tr>
                        <tr>
                            <th class="has-text-right pt-3">
                                <span class="has-tooltip-multiline" data-tooltip="A list of ability IDs to be run on a new agent beacon. By default, this is set to run a command which clears command history.">Bootstrap Abilities</span>
                            </th>
                            <td>
                                <div class="field is-grouped is-grouped-multiline" x-show="!isAddingBootstrapAbility || bootstrapAbilities.length">
                                    <template x-for="(abilityId, index) of bootstrapAbilities">
                                        <div class="control">
                                            <div class="tags has-addons">
                                                <a class="tag is-info" x-text="getAbilityName(abilityId)" x-bind:data-tooltip="getAbilityDescription(abilityId)"></a>
                                                <a class="tag is-delete" @click="bootstrapAbilities.splice(index, 1)"></a>
                                            </div>
                                        </div>
                                    </template>
                                    <div class="control">
                                        <div class="tags has-addons">
                                            <a class="tag is-primary" @click="isAddingBootstrapAbility = true; isAddingDeadmanAbility = false" x-show="!isAddingBootstrapAbility">
                                                <span class="icon"><i class="fas fa-plus"></i></span>
                                            </a>
                                        </div>
                                    </div>
                                </div>
                                <div class="field has-addons" x-show="isAddingBootstrapAbility">
                                    <div class="control is-expanded">
                                        <input class="input is-small" placeholder="Search for an ability..." x-model="abilitySearchQuery" x-on:keyup="searchForAbility()">
                                        <div class="search-results">
                                            <template x-for="result of abilitySearchResults" :key="result.ability_id">
                                                <p @click="addBootstrapAbility(result.ability_id)" x-text="result.name"></p>
                                            </template>
                                        </div>
                                    </div>
                                    <div class="control">
                                        <a class="button is-small" @click="isAddingBootstrapAbility = false">Cancel</a>
                                    </div>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="has-text-right pt-3">
                                <span class="has-tooltip-multiline" data-tooltip="A list of abilities to be run immediately prior to agent termination. The agent must support deadman abilities in order for them to run.">Deadman Abilities</span>
                            </th>
                            <td>
                                <div class="field is-grouped is-grouped-multiline" x-show="!isAddingDeadmanAbility || deadmanAbilities.length">
                                    <template x-for="(abilityId, index) of deadmanAbilities">
                                        <div class="control">
                                            <div class="tags has-addons">
                                                <a class="tag is-info" x-text="getAbilityName(abilityId)" x-bind:data-tooltip="getAbilityDescription(abilityId)"></a>
                                                <a class="tag is-delete" @click="deadmanAbilities.splice(index, 1)"></a>
                                            </div>
                                        </div>
                                    </template>
                                    <div class="control">
                                        <div class="tags has-addons">
                                            <a class="tag is-primary" @click="isAddingDeadmanAbility = true; isAddingBootstrapAbility = false" x-show="!isAddingDeadmanAbility">
                                                <span class="icon"><i class="fas fa-plus"></i></span>
                                            </a>
                                        </div>
                                    </div>
                                </div>
                                <div class="field has-addons" x-show="isAddingDeadmanAbility">
                                    <div class="control is-expanded">
                                        <input class="input is-small" placeholder="Search for an ability..." x-model="abilitySearchQuery" x-on:keyup="searchForAbility()">
                                        <div class="search-results">
                                            <template x-for="result of abilitySearchResults" :key="result.ability_id">
                                                <p @click="addDeadmanAbility(result.ability_id)" x-text="result.name"></p>
                                            </template>
                                        </div>
                                    </div>
                                    <div class="control">
                                        <a class="button is-small" @click="isAddingDeadmanAbility = false">Cancel</a>
                                    </div>
                                </div>
                            </td>
                        </tr>
                    </tbody>
                </table>
                <p class="help">* A global setting that will effect any new or existing agents.</p>
            </section>
            <footer class="modal-card-foot">
                <nav class="level">
                    <div class="level-left"></div>
                    <div class="level-right">
                        <div class="level-item">
                            <button class="button is-small" @click="showConfigModal = false">Close</button>
                        </div>
                        <div class="level-item">
                            <button class="button is-primary is-small" @click="saveAgentConfig()">
                                <span class="icon"><i class="fas fa-save"></i></span>
                                <span>Save</span>
                            </button>
                        <div>
                    </div>
                </nav>
            </footer>
        </div>
    </div>

    <div class="modal" x-bind:class="{ 'is-active': showDetailsModal }">
        <div class="modal-background" @click="showDetailsModal = false"></div>
        <div class="modal-card">
            <header class="modal-card-head">
                <p class="modal-card-title">Agent Details</p>
            </header>
            <section class="modal-card-body">
                <p class="has-text-weight-bold">Settings</p>
                <table>
                    <col width="30%">
                    <col width="70%">
                    <tbody>
                        <tr>
                            <th class="has-text-right pt-3">Contact</th>
                            <td>
                                <div class="select is-small">
                                    <select x-model="selectedAgent.pending_contact">
                                        <template x-for="contact of selectedAgent.available_contacts" :key="contact">
                                            <option x-bind:value="contact" x-text="contact"></option>
                                        </template>
                                    </select>
                                </div>
                            </td>
                        </tr>
                        <tr>
                            <th class="has-text-right pt-3">Group</th>
                            <td>
                                <input class="input is-small" type="text" x-model="selectedAgent.group" x-bind:class="{ 'is-danger': fieldErrorsAgent.includes('group') }">
                                <p x-show="fieldErrorsAgent.includes('group')" class="help is-danger">This field is required.</p>
                            </td>
                        </tr>
                        <tr>
                            <th class="has-text-right pt-3">Sleep Timer</th>
                            <td>
                                <div class="is-align-items-center is-flex is-align-items-center">
                                    <span class="top-right-anchor">
                                        <label>min</label>
                                        <input class="input is-small is-small-number" type="number" placeholder="30" x-model="selectedAgent.sleep_min" x-bind:class="{ 'is-danger': fieldErrorsAgent.includes('sleep_min') }">
                                    </span>
                                    <span class="top-right-anchor">
                                        <label>max</label>
                                        <input class="input is-small is-small-number" type="number" placeholder="60" x-model="selectedAgent.sleep_max" x-bind:class="{ 'is-danger': fieldErrorsAgent.includes('sleep_max') }">                                     
                                    </span>
                                </div>
                                <p x-show="fieldErrorsAgent.includes('sleep_min') || fieldErrorsAgent.includes('sleep_max')" class="help is-danger">This field is required.</p>
                            </td>
                        </tr>
                        <tr>
                            <th class="has-text-right pt-3">Watchdog Timer</th>
                            <td>
                                <input class="input is-small is-small-number" type="number" x-model="selectedAgent.watchdog" x-bind:class="{ 'is-danger': fieldErrorsAgent.includes('watchdog') }">
                                <p x-show="fieldErrorsAgent.includes('watchdog')" class="help is-danger">This field is required.</p>
                            </td>
                        </tr>
                    </tbody>
                </table>
                <button class="button is-small is-fullwidth is-primary" @click="saveAgent()">Save Settings</button>
                <hr>
                <p class="has-text-weight-bold">Agent Details</p>
                <table>
                    <col width="30%">
                    <col width="70%">
                    <tbody>
                        <tr>
                            <th class="has-text-right">Status</th>
                            <td>
                                <span x-text="getAgentStatus(selectedAgent)" x-bind:class="{ 'has-text-warning': getAgentStatus(selectedAgent) === 'dead', 'has-text-success': getAgentStatus(selectedAgent) === 'alive', 'has-text-info': getAgentStatus(selectedAgent) === 'pending kill' }"></span><span>,</span>
                                <span x-text="selectedAgent.trusted ? 'trusted' : 'untrusted'" x-bind:class="{ 'has-text-warning': !selectedAgent.trusted, 'has-text-success': selectedAgent.trusted }"></span>
                            </td>
                        </tr>
                        <tr>
                            <th class="has-text-right">Paw</th>
                            <td x-text="selectedAgent.paw"></td>
                        </tr>
                        <tr>
                            <th class="has-text-right">Host</th>
                            <td x-text="`${selectedAgent.host} (${selectedAgent.host_ip_addrs.join(', ')})`"></td>
                        </tr>
                        <tr>
                            <th class="has-text-right">Display Name</th>
                            <td x-text="selectedAgent.display_name"></td>
                        </tr>
                        <tr>
                            <th class="has-text-right">Username</th>
                            <td x-text="selectedAgent.username"></td>
                        </tr>
                        <tr>
                            <th class="has-text-right">Privilege</th>
                            <td x-text="selectedAgent.privilege"></td>
                        </tr>
                        <tr>
                            <th class="has-text-right">Last Seen</th>
                            <td x-text="selectedAgent.last_seen"></td>
                        </tr>
                        <tr>
                            <th class="has-text-right">Created</th>
                            <td x-text="selectedAgent.created"></td>
                        </tr>
                        <tr>
                            <th class="has-text-right">Architecture</th>
                            <td x-text="selectedAgent.architecture"></td>
                        </tr>
                        <tr>
                            <th class="has-text-right">Platform</th>
                            <td x-text="selectedAgent.platform"></td>
                        </tr>
                        <tr>
                            <th class="has-text-right">PID</th>
                            <td x-text="selectedAgent.pid"></td>
                        </tr>
                        <tr>
                            <th class="has-text-right">PPID</th>
                            <td x-text="selectedAgent.ppid"></td>
                        </tr>
                        <tr>
                            <th class="has-text-right">Executable Name</th>
                            <td x-text="selectedAgent.exe_name"></td>
                        </tr>
                        <tr>
                            <th class="has-text-right">Location</th>
                            <td x-text="selectedAgent.location"></td>
                        </tr>
                        <tr>
                            <th class="has-text-right">Executors</th>
                            <td x-text="selectedAgent.executors"></td>
                        </tr>
                        <tr>
                            <th class="has-text-right">Host IP Addresses</th>
                            <td x-text="selectedAgent.host_ip_addrs"></td>
                        </tr>
                        <tr>
                            <th class="has-text-right">Peer-to-Peer Proxy Receivers</th>
                            <td x-text="(selectedAgent.proxy_receivers && Object.keys(selectedAgent.proxy_receivers).length) ? Object.keys(selectedAgent.proxy_receivers) : 'No local P2P proxy receivers active.'"></td>
                        </tr>
                        <tr>
                            <th class="has-text-right">Peer-to-Peer Proxy Chain</th>
                            <td x-text="(selectedAgent.proxy_chain && selectedAgent.proxy_chain.length) ? selectedAgent.proxy_chain.join(', ') : 'Not using P2P agents to reach C2.'"></td>
                        </tr>
                    </tbody>
                </table>
            </section>
            <footer class="modal-card-foot">
                <nav class="level">
                    <div class="level-left">
                        <div class="level-item">
                            <button class="button is-danger is-outlined is-small has-text-white" @click="confirmAlt(`Are you sure you want to kill agent '${selectedAgent.paw}'?`, () => killAgent(selectedAgent))">
                                <span class="icon"><i class="fas fa-skull-crossbones"></i></span>
                                <span>Kill Agent</span>
                            </button>
                        </div>
                    </div>
                    <div class="level-right">
                        <div class="level-item">
                            <button class="button is-small" @click="showDetailsModal = false">Close</button>
                        <div>
                    </div>
                </nav>
            </footer>
        </div>
    </div>

    <div class="modal" x-bind:class="{ 'is-active': showConfirmModal }">
        <div class="modal-background" @click="showConfirmModal = false"></div>
        <div class="modal-card" style="z-index: 20">
            <header class="modal-card-head">
                <p class="modal-card-title">Confirm</p>
            </header>
            <section class="modal-card-body">
                <p x-text="confirmText"></p>
            </section>
            <footer class="modal-card-foot">
                <nav class="level">
                    <div class="level-left">
                        <div class="level-item">
                            <button class="button is-small" @click="showConfirmModal = false">Cancel</button>
                        </div>
                    </div>
                    <div class="level-right">
                        <div class="level-item">
                            <button class="button is-primary is-small" @click="confirmFunction(); showConfirmModal = false;">Confirm</button>
                        <div>
                    </div>
                </nav>
            </footer>
        </div>
    </div>

</div>

<script>
    function alpineAgents() {
        return {
            // Core variables
            agents: [],
            abilities: [],
            deploymentAbilities: [],
            selectedAbilityId: '',
            selectedAbility: {},
            platforms: [],
            selectedPlatform: '',
            selectedAgent: { available_contacts: [] },

            agentConfig: { bootstrap_abilities: [] },
            agentFields: [],
            agentFieldConfig: {},
            deployCommands: [],
            filteredCommands: [],

            bootstrapAbilities: [],
            deadmanAbilities: [],
            isAddingBootstrapAbility: false,
            isAddingDeadmanAbility: false,
            abilitySearchQuery: [],
            abilitySearchResults: [],

            isHoveringAgentCount: false,

            // Modals
            showDeployModal: false,
            showConfigModal: false,
            showDetailsModal: false,
            showConfirmModal: false,
            confirmText: '',
            confirmFunction: () => {},

            // Input validation
            requiredFieldsConfig: ['sleep_min', 'sleep_max', 'watchdog', 'untrusted_timer', 'implant_name'],
            fieldErrorsConfig: [],
            requiredFieldsAgent: ['group', 'sleep_min', 'sleep_max', 'watchdog'],
            fieldErrorsAgent: [],

            initPage() {
                apiV2('GET', '/api/v2/agents').then((agents) => {
                    this.agents = agents;
                    this.sortAgents();
                    return apiV2('GET', '/api/v2/config/agents');
                }).then((agentConfig) => {
                    this.agentConfig = agentConfig;
                    this.bootstrapAbilities = this.agentConfig.bootstrap_abilities || [];
                    this.deadmanAbilities = this.agentConfig.deadman_abilities || [];
                    return apiV2('GET', '/api/v2/abilities');
                }).then(async (allAbilities) => {
                    this.abilities = allAbilities;
                    this.agentConfig.deployments.forEach((abilityId) => {
                        let match = allAbilities.find((ability) => ability.ability_id === abilityId);
                        if (match) this.deploymentAbilities.push(match);
                    });

                    // While the agents tab is open, keep checking for new/killed agents
                    while (this.$refs.header) {
                        await sleep(3000);
                        this.refreshAgents();
                    }
                }).catch((error) => {
                    toast('Error initializing page', false);
                    console.error(`Error initializing page: ${error}`);
                });
            },

            refreshAgents() {
                apiV2('GET', '/api/v2/agents').then((agents) => {
                    this.agents = agents;
                    this.sortAgents();
                }).catch((error) => {
                    toast('Error refreshing agents', false);
                    console.error(error);
                });
            },

            sortAgents() {
                this.agents.sort((a, b) => new Date(b.last_seen.replace(/-/g, "/")).getTime() - new Date(a.last_seen.replace(/-/g, "/")).getTime());
            },

            selectAbility() {
                this.selectedAbility = this.deploymentAbilities.find((ability) => ability.ability_id === this.selectedAbilityId);
                this.platforms = [...new Set(this.selectedAbility.executors.map((executor) => executor.platform))];
                if (!this.platforms.includes(this.selectedPlatform)) this.selectedPlatform = '';

                apiV2('GET', `/api/v2/deploy_commands/${this.selectedAbilityId}`).then((data) => {
                    this.deployCommands = data.abilities;
                    this.agentFieldConfig = data.app_config;
                    this.filterAbilityPlatforms();
                }).catch((error) => {
                    toast('Error loading the agent.', false);
                    console.error(`Error loading the agent: ${error}`);
                });
            },

            changePlatform(platform) {
                this.selectedPlatform = platform;
                this.filterAbilityPlatforms();
            },

            filterAbilityPlatforms() {
                if (this.selectedPlatform === 'all') {
                    this.filteredCommands = this.deployCommands;
                } else {
                    this.filteredCommands = this.deployCommands.filter((ability) => ability.platform === this.selectedPlatform);
                }

                let fields = [];
                this.agentFields = [];
                this.filteredCommands.forEach((command) => {
                    fields = fields.concat([...command.command.matchAll(/#{(.*?)}/gm)].map((field) => field[1]));
                    command.variations.forEach((variation) => {
                        fields = fields.concat([...variation.command.matchAll(/#{(.*?)}/gm)].map((field) => field[1]));
                    })
                });
                fields = [...new Set(fields)];
                fields.forEach((field) => {
                    this.agentFields.push({ name: field, value: this.agentFieldConfig[field] });
                });
            },

            closeDeployModal() {
                this.showDeployModal = false;
                this.selectedAbilityId = '';
                this.selectedPlatform = '';
                this.filteredCommands = [];
                this.agentFields = [];
            },

            getCommandField(command) {
                this.agentFields.forEach((field) => {
                    command = command.replaceAll(`#{${field.name}}`, field.value);
                });
                return command;
            },

            copyCommandToClipboard(command) {
                navigator.clipboard.writeText(command.replaceAll('\n', ''));
                toast('Copied command to clipboard!', true);
            },

            getAbilityName(id) {
                let abil = this.abilities.find((ability) => ability.ability_id === id);
                return (!id || !this.abilities.length || !abil) ? '[unknown]' : abil.name;
            },

            getAbilityDescription(id) {
                let abil = this.abilities.find((ability) => ability.ability_id === id);
                return (!id || !this.abilities.length || !abil) ? 'The current user does not have permissions to view this ability.' : abil.description;
            },

            searchForAbility() {
                this.abilitySearchResults = [];
                if (!this.abilitySearchQuery) return;
                this.abilities.forEach((ability) => {
                    if (ability.name.toLowerCase().indexOf(this.abilitySearchQuery.toLowerCase()) > -1) {
                        this.abilitySearchResults.push({
                            ability_id: ability.ability_id,
                            name: ability.name
                        });
                    }
                });
            },

            addBootstrapAbility(id) {
                this.bootstrapAbilities.push(id);
                this.abilitySearchQuery = '';
                this.abilitySearchResults = [];
                this.isAddingBootstrapAbility = false;
            },

            addDeadmanAbility(id) {
                this.deadmanAbilities.push(id);
                this.abilitySearchQuery = '';
                this.abilitySearchResults = [];
                this.isAddingDeadmanAbility = false;
            },

            getAgentStatus(agent) {
                if (!agent.last_seen) return '';
                let lastSeen = new Date(agent.last_seen).getTime();
                let msSinceSeen = Date.now() - lastSeen;
                // Give a buffer of 1 minute to mark an agent dead
                let isAlive = (msSinceSeen < (agent.sleep_max * 1000));

                if (msSinceSeen <= 60000 && agent.sleep_min === 3 && agent.sleep_max === 3 && agent.watchdog === 1) {
                    return 'pending kill'
                } else {
                    return msSinceSeen <= 60000 || isAlive ? 'alive' : 'dead';
                }
            },

            saveAgent() {
                this.fieldErrorsAgent = validateInputs(this.selectedAgent, this.requiredFieldsAgent);
                if (this.fieldErrorsAgent.length) return;

                apiV2('PATCH', `/api/v2/agents/${this.selectedAgent.paw}`, this.selectedAgent).then((response) => {
                    toast('Agent saved', true);
                }).catch((error) => {
                    toast('Error killing the agent', false);
                    console.error(error);
                });
            },

            saveAgentConfig() {
                this.fieldErrorsConfig = validateInputs(this.agentConfig, this.requiredFieldsConfig);
                if (this.fieldErrorsConfig.length) return;

                let reqBody = { 
                    ...this.agentConfig,
                    bootstrap_abilities: this.bootstrapAbilities,
                    deadman_abilities: this.deadmanAbilities
                };
                delete reqBody.deployments;
                apiV2('PATCH', '/api/v2/config/agents', reqBody).then((response) => {
                    this.showConfigModal = false;
                    toast('Configuration saved.', true);
                }).catch((error) => {
                    toast('Error saving the configuration.', false);
                    console.error(error);
                });
            },

            removeAgent(paw, index) {
                apiV2('DELETE', `/api/v2/agents/${paw}`).then(() => {
                    this.agents.splice(index, 1);
                }).catch((error) => {
                    toast('Error removing the agent', false);
                    console.error(error);
                });
            },

            confirmAlt(msg, fn) {
                this.confirmText = msg;
                this.confirmFunction = fn;
                this.showConfirmModal = true;
            },

            removeDeadAgents() {
                this.agents.forEach((agent, index) => {
                    if (this.getAgentStatus(agent) === 'dead') {
                        this.removeAgent(agent.paw, index);
                    }
                });
            },

            removeAllAgents() {
                this.agents.forEach((agent, index) => {
                    this.removeAgent(agent.paw, index);
                });
            },

            killAgent(agent) {
                let reqBody = {
                    watchdog: 1,
                    sleep_min: 3,
                    sleep_max: 3
                };

                apiV2('PATCH', `/api/v2/agents/${agent.paw}`, reqBody).then((response) => {
                    toast('Agent will be killed after its next beacon.', true);
                }).catch((error) => {
                    toast('Error killing the agent', false);
                    console.error(error);
                });
                this.showDetailsModal = false;
            },
        
            killAllAgents() {
                let reqBody = {
                    watchdog: 1,
                    sleep_min: 3,
                    sleep_max: 3
                };
                let numKilled = 0;

                this.agents.forEach((agent, index) => {
                    apiV2('PATCH', `/api/v2/agents/${agent.paw}`, reqBody).then((response) => {
                        numKilled++;
                        if (numKilled === this.agents.length) {
                            toast('All agents have been configured to be killed by next beacon.', true);
                        }
                    }).catch((error) => {
                        toast('Error killing the agent', false);
                        console.error(error);
                    });
                });
            }
        };
    }

    // # sourceURL=agents.js
</script>

<style>
    
    pre.box {
        margin-bottom: 0;
        margin-top: 10px;
        scrollbar-color: #3f3f3f #0000;
        scrollbar-width: thin;
    }

    .command-container {
        background-color: #262626;
        border-radius: 4px;
        margin: 30px;
    }

    .command-fields {
        padding: 0 250px;
    }

    .is-small-number {
        width: 80px;
    }

    .is-white {
        background-color: #636363;
    }
    .is-white:hover {
        background-color: #505050 !important;
    }

    .cmd-copy-button {
        position: absolute;
        top: 10px;
        right: 10px;
    }

    .platform {
        width: 80px;
        border-radius: 4px;
        padding: 5px;
        margin: 5px;
        font-family: monospace;
        border: 1px solid transparent;
    }
    .platform:hover {
        background-color: #484848;
        cursor: pointer; 
    }
    .platform.selected {
        border: 1px solid white;
    }

    .pointer {
        cursor: pointer;
    }

    .search-results {
        overflow-y: scroll;
        max-height: 200px;
        background-color: #010101;
        border-radius: 0 4px;
    }
    .search-results p {
        margin-bottom: 0 !important;
        padding: 5px;
        cursor: pointer;
    }
    .search-results p:hover {
        background-color: #484848;
    }

    .tag.blue {
        background-color: #191970 !important;
    }
    .tag.red {
        background-color: #8B0000 !important;
    }

    .top-right-anchor > label {
        position: relative;
        top: -14px;
        left: 75%;
        color: #a0a0a0;
        z-index: 999;
        font-size: .8em;
    }
    .top-right-anchor {
        margin-left: -26px;
        padding-right: 5px;
    }

</style>
